#include <pyloader.h>
static InputContext* GetInputContextFromPyObject(PyObject* object)
{
	if(NULL == object) return NULL;
	if(!PyObject_HasAttrString(object,"__address__")) return NULL;
	PyObject* __address__ = PyObject_GetAttrString(object,"__address__");
	if(NULL == __address__ || 
	   !PyByteArray_Check(__address__) ||
	   PyByteArray_GET_SIZE(__address__) != sizeof(void*))
	{
		if(__address__) Py_DECREF(__address__);
		return NULL;
	}
	InputContext* IC = *(InputContext**)PyByteArray_AsString(__address__);
	Py_DECREF(__address__);
	return IC;
}
static char** GenerateStringListFromPyObject(PyObject* object, int *nActual)
{
	if(NULL == object || !PyList_Check(object)) return NULL;
	size_t len = PyObject_Length(object);
	char** ret = (char**)malloc(sizeof(char*)*len);
	PyObject* iterator = PyObject_GetIter(object);
	PyObject* item;
	*nActual = 0;
	while((item = PyIter_Next(iterator)))
	{
		if(PyString_Check(item)) ret[(*nActual)++] = PyString_AsString(item);
		Py_DECREF(item);
	}
	Py_DECREF(iterator);
	return ret;
}
//Convert Vine APIs into Python form
static PyObject* VPL_Commit(PyObject* self, PyObject* args)
{
	PyObject* object; //we borrow the reference from caller
	char *str;
	if(PyArg_ParseTuple(args,"Os",&object,&str) == 0)
	{
		return NULL;
	}
	InputContext* IC = GetInputContextFromPyObject(object);
	if(NULL == IC) return NULL;
	if(-1 == IMCommit(str,IC)) return NULL;
	Py_RETURN_NONE;
}
static PyObject* VPL_UpdatePrecommitText(PyObject* self, PyObject* args)
{
	PyObject* object; //we borrow the reference from caller
	char *str;
	if(PyArg_ParseTuple(args,"Os",&object,&str) == 0)
	{
		return NULL;
	}
	InputContext* IC = GetInputContextFromPyObject(object);
	if(NULL == IC) return NULL;
	if(-1 == IMUpdatePrecommitText(str,IC)) return NULL;
	Py_RETURN_NONE;
}
static PyObject* VPL_SetTrayIcon(PyObject* self, PyObject* args)
{
	PyObject* object; //we borrow the reference from caller
	char *str;
	if(PyArg_ParseTuple(args,"Os",&object,&str) == 0)
	{
		return NULL;
	}
	InputContext* IC = GetInputContextFromPyObject(object);
	if(NULL == IC) return NULL;
	if(-1 == IMSetTrayIcon(str,IC)) return NULL;
	Py_RETURN_NONE;
}
static PyObject* VPL_UpdateCandidate(PyObject* self, PyObject* args)
{
	PyObject* object;
	PyObject* list;
	if(!PyArg_ParseTuple(args,"OO",&object,&list))
	{
		return NULL;
	}
	InputContext* IC = GetInputContextFromPyObject(object);
	if(NULL == IC) return NULL;
	int nCandidate;
	char** cl = GenerateStringListFromPyObject(list,&nCandidate);
	if(NULL == list) return NULL;
	int rc = IMUpdateCandidates(cl,nCandidate,IC);
	free(cl);
	if(rc == -1) return NULL;
	Py_RETURN_NONE;

}
int VPL_genericforwardcallback(uint32_t sym, uint32_t state, uint32_t id, void* data)
{
	InputContext* IC = (InputContext*)data;
	int i,j;
	for(i = id%MAXHAND , j = 0; 
	    j < MAXHAND && IC->handler_list[i].id != id;
	    j ++, i = (i+1)%MAXHAND);
	if(j == MAXHAND) return 0;
	PyObject* func = IC->handler_list[i].callable;
	if(NULL == func) return 0;
	PyObject* args = Py_BuildValue("OIII",IC->pContextObject,sym,state,id);
	PyObject* ret = PyObject_Call(func, args, NULL);
	Py_DECREF(args);
	if(ret == NULL) 
	{
		PyErr_Print();
		return -1;
	}
	int rc = FE_SUCCEED;
	if(PyInt_Check(ret))
		rc = PyInt_AsLong(ret);
	Py_DECREF(ret);
	return rc;
}
void VPL_ClearCallBackList(void* data)
{
	int i;
	InputContext* IC = (InputContext*)data;
	for(i = 0; i < MAXHAND; i ++)
	{
		if(IC->handler_list[i].callable)
		{
			Py_DECREF(IC->handler_list[i].callable);
		}
		IC->handler_list[i].callable = NULL;
		IC->handler_list[i].id = -1;
	}
}
static PyObject* VPL_RegisterForwardFilter(PyObject* self, PyObject* arg)
{
	PyObject* object,*func;
	uint32_t syml,symr,neg,pos,rel;
	if(!PyArg_ParseTuple(arg,"OIIIIIO",&object,&syml,&symr,&neg,&pos,&rel,&func)) return NULL;
	if(!PyCallable_Check(func)) return NULL;
	InputContext* IC = GetInputContextFromPyObject(object);
	if(NULL == IC) return NULL;
	int rc = IMRegisterForwardFilter(syml,symr,neg,pos,rel,VPL_genericforwardcallback,IC);
	if(rc >= 0)
	{
		int i,j;
		for(i = rc%MAXHAND, j = 0; 
		    j < MAXHAND && IC->handler_list[i].id != -1; 
		    j ++, i = (i+1)%MAXHAND);
		if(j < MAXHAND)
		{
			IC->handler_list[i].id = rc;
			IC->handler_list[i].callable = func;
			Py_INCREF(func);
		}
		PyObject* ret_value = Py_BuildValue("i",rc);
		return ret_value;
	}
	Py_RETURN_NONE;
}
static PyObject* VPL_SwitchForwardFilter(PyObject* self,PyObject* args)
{
	PyObject* object;
	int id,en;
	if(!PyArg_ParseTuple(args,"Oii",&object,&id,&en))
	{
		return NULL;
	}
	InputContext* IC = (InputContext*)GetInputContextFromPyObject(object);
	if(NULL == IC) return NULL;
	IMSwitchForwardFilter(id,en,IC);
	Py_RETURN_NONE;
}
static PyObject* VPL_ResetForwardFilter(PyObject* self,PyObject* args)
{
	PyObject* object;
	if(!PyArg_ParseTuple(args,"O",&object))
	{
		return NULL;
	}
	InputContext* IC = (InputContext*)GetInputContextFromPyObject(object);
	if(NULL == IC) return NULL;
	IMResetForwardFilter(IC);
	VPL_ClearCallBackList(IC);
	Py_RETURN_NONE;
}
PyObject* ICGetCallale(void* data,const char* name)
{
	InputContext* ic = (InputContext*)data;
	PyObject* handler = PyObject_GetAttrString(ic->pContextObject, name);
	if(handler == NULL) 
	{
		PyErr_Print();
		return NULL;
	}
        if(!PyCallable_Check(handler)) 
	{
		Py_DECREF(handler);
		return NULL;
	}
	return handler;
}
typedef struct {
	char* name;
	int  value;
} VineConst_t;
void InitPython(void* IC)
{
	static PyMethodDef VineMethod[] = {
		{"Commit", VPL_Commit, METH_VARARGS, ""},
		{"UpdatePrecommitText", VPL_UpdatePrecommitText,METH_VARARGS, ""},
		{"UpdateCandidates" , VPL_UpdateCandidate,METH_VARARGS,""},
		{"SetTrayIcon", VPL_SetTrayIcon, METH_VARARGS,""},
		{"RegisterForwardFilter",VPL_RegisterForwardFilter,METH_VARARGS,""},
		{"SwitchForwardFilter",VPL_SwitchForwardFilter,METH_VARARGS,""},
		{"ResetForwardFilter", VPL_ResetForwardFilter,METH_VARARGS,""},
		{NULL,NULL,0,NULL}
	};
#define __CST__(name) {#name,name},
#define __ENDCST__ {NULL,0}
	static VineConst_t VineConst[] = {
		__CST__(FE_SUCCEED)
		__CST__(FE_CLIENT)
		__CST__(FE_SYSTEM)
		__CST__(IMKeyActionPress)
		__CST__(IMKeyActionRelease)
		__CST__(IMKeyActionAny)
		__CST__(IMStateNone)
		__CST__(IMStateShift)
		__CST__(IMStateCaps)
		__CST__(IMStateCtrl)
		__CST__(IMStateAlt)
		__CST__(IMStateNum)
		__CST__(IMStateSuper)
		__CST__(IMStateScroll)
		__CST__(IMStateAll)
		__CST__(IMSymBackSpace)
		__CST__(IMSymTab)
		__CST__(IMSymLinefeed)
		__CST__(IMSymClear)
		__CST__(IMSymReturn)
		__CST__(IMSymPause)
		__CST__(IMSymScrollLock)
		__CST__(IMSymSysReq)
		__CST__(IMSymEscape)
		__CST__(IMSymHome)
		__CST__(IMSymLeft)
		__CST__(IMSymUp)
		__CST__(IMSymRight)
		__CST__(IMSymDown)
		__CST__(IMSymPageUp)
		__CST__(IMSymPageDown)
		__CST__(IMSymEnd)
		__CST__(IMSymMenu)
		__CST__(IMNumLock)
		__CST__(IMSymEqual)
		__CST__(IMSymMultiply)
		__CST__(IMSymAdd)
		__CST__(IMSymSeparator)
		__CST__(IMSymSubtract)
		__CST__(IMSymDecimal)
		__CST__(IMSymDivide)
		__CST__(IMSymKeypad0)
		__CST__(IMSymKeypad1)
		__CST__(IMSymKeypad2)
		__CST__(IMSymKeypad3)
		__CST__(IMSymKeypad4)
		__CST__(IMSymKeypad5)
		__CST__(IMSymKeypad6)
		__CST__(IMSymKeypad7)
		__CST__(IMSymKeypad8)
		__CST__(IMSymKeypad9)
		__CST__(IMSymF1)
		__CST__(IMSymF2)
		__CST__(IMSymF3)
		__CST__(IMSymF4)
		__CST__(IMSymF5)
		__CST__(IMSymF6)
		__CST__(IMSymF7)
		__CST__(IMSymF8)
		__CST__(IMSymF9)
		__CST__(IMSymF10)
		__CST__(IMSymF11)
		__CST__(IMSymF12)
		__CST__(IMSymShiftL)
		__CST__(IMSymShiftR)
		__CST__(IMSymControlL)
		__CST__(IMSymControlR)
		__CST__(IMSymCapsLock)
		__CST__(IMSymShiftLock)
		__CST__(IMSymMetaL)
		__CST__(IMSymMetaR)
		__CST__(IMSymAltL)
		__CST__(IMSymAltR)
		__CST__(IMSymSuperL)
		__CST__(IMSymSuperR)
		__CST__(IMSymHyperL)
		__CST__(IMSymHyperR)
		__CST__(IMSymDelete)
		__ENDCST__};
		
	Py_Initialize();
	PyObject* path = PySys_GetObject("path");
	char* s_path; 
	IMReadConfigure("SearchPath", IMConfString, &s_path, "", IC);
	char pathbuf[256];
	int i, p = 0;
	for(i = 0;; i ++)
	{
		if(s_path[i] == ':' || s_path[i] == 0) 
		{
			pathbuf[p] = 0;
			PyObject* tmp_str = PyString_FromString(pathbuf);
			IM_LOG_DEBUG("Python Module Search Python : %s",pathbuf);
			PyList_Append(path, tmp_str);
			Py_DECREF(tmp_str);
			p = 0;
			if(s_path[i] == 0) break;
			continue;
		}
		pathbuf[p++] = s_path[i];
	}
	PyObject* vine_module = Py_InitModule("Vine", VineMethod);
	for(i = 0; VineConst[i].name ; i ++)
		PyModule_AddIntConstant(vine_module, VineConst[i].name, VineConst[i].value);
}
int DestoryIMObject(void* ic)
{
	InputContext* IC = (InputContext*)ic;
	if(IC->pModule) Py_DECREF(IC->pModule);
	if(IC->pContextObject) Py_DECREF(IC->pContextObject);
	return 0;
}
int CreateIMObject(void* ic)
{
	InputContext* context = (InputContext*)ic;
	context->pModule = PyImport_ImportModule(IMGetIMMetaData(Parameter,ic));
	if(NULL == context->pModule) 
	{
		PyErr_Print();
		return -1;
	}
	PyObject* IMEMain = PyObject_GetAttrString(context->pModule, "Create");
	if(!IMEMain || !PyCallable_Check(IMEMain))
	{
		if(IMEMain) Py_DECREF(IMEMain);
		return -1;
	}
	PyObject* args = PyTuple_New(0);
	context->pContextObject = PyObject_Call(IMEMain,args,NULL);
	Py_DECREF(args);
	Py_DECREF(IMEMain);
	if(NULL == context->pContextObject) 
	{
		PyErr_Print();
		return -1;
	}
	PyObject* address = PyByteArray_FromStringAndSize((char*)&ic,sizeof(void*));
	PyObject_SetAttrString(context->pContextObject,"__address__",address);
	Py_DECREF(address);
	int i;
	for(i = 0; i < MAXHAND; i ++)
		context->handler_list[i].id = -1;
	return 0;
}
